/* Measure.h
 *
 * Created on: Mar 12, 2012
 * Author: snirgaz
 */

#ifndef MEASURE_H_
#define MEASURE_H_
#include "../HelpLibs/Observable.h"
#include "Lattice.h"
#include "../HelpLibs/QmcParameters.h"
#include "../HelpLibs/def.h"
#include "../HelpLibs/fftwPlane.h"
#include "boost/chrono.hpp"
#include <map>
#include<functional>
#include "boost/rational.hpp"
using namespace std;

typedef boost::chrono::system_clock::time_point TimePoint;
typedef boost::chrono::duration<double, boost::ratio<3600> > Hour;
typedef boost::rational<int> rationalNumber;
class ProgressBar {
	int totalNumberOfMeasure_;
	ofstream *outFile_;
	double simTime_;
	TimePoint startTime_;
	double pc_;
public:
	void init(int totalNumberOfMeasure, int minNumOfMeasure, double simTime,
			ofstream *outFile);
	void update(int numOfMeasure, bool &endTime, bool &dumpFlag);
	void updateMinNumOfMeasure(int minNumOfMeasure);
};
enum MeasureType {
	OPEN, CLOSED
};

class Measure {
protected:
	MeasureType measureType_;
	Lattice *lat_;
public:
	Measure();
	virtual void DoMeasurement()=0;
	virtual bool checkDone() const =0;
	virtual void dumpToFile(hdf5RW &hdf5File) const=0;
	virtual int getNumOfMeasure() const=0;
	virtual void readFromFile(hdf5RW &hdf5File)=0;
	MeasureType getMeasureType() const;
	virtual ~Measure();
};

//class GreenMeasure: public Measure {
//	typedef ArrayObservable<Observable<ScalarObs<double> >, DIM> obs;
//	typedef obs::IterIndex obsIterIndex;
//	typedef Observable<ScalarObs<double> >::InitData InitData;
//	obs green_;
//	obs susp_;
//public:
//	GreenMeasure(Lattice *lat, QmcParameters &qmcParameters);
//	~GreenMeasure();
//	void DoMeasurement();
//	void dumpToFile(hdf5RW &hdf5File) const;
//	void readFromFile(hdf5RW &hdf5File);
//	bool checkDone() const;
//	int getNumOfMeasure() const;
//};
//
//class ZZPRatioMeasure: public Measure {
//	Observable<ScalarObs<double> > ZZPRatio_;
//	BoltzWeightType boltzRatio_;
//public:
//	ZZPRatioMeasure(Lattice *lat, QmcParameters &qmcParameters,
//			BoltzWeightType boltzRatio);
//	~ZZPRatioMeasure();
//	void DoMeasurement();
//	void dumpToFile(hdf5RW &hdf5File) const;
//	void readFromFile(hdf5RW &hdf5File);
//	bool checkDone() const;
//	int getNumOfMeasure() const;
//};
//class SuseptibilityMeasure: public Measure {
//	typedef Observable<ScalarObs<double> > obs;
//	BoltzWeightType boltzRatio_;
//	obs susp_;
//public:
//	SuseptibilityMeasure(Lattice *lat, QmcParameters &qmcParametersg,
//			BoltzWeightType boltzRatio);
//	SuseptibilityMeasure();
//	void DoMeasurement();
//	void dumpToFile(hdf5RW &hdf5File) const;
//	void readFromFile(hdf5RW &hdf5File);
//	bool checkDone() const;
//	int getNumOfMeasure() const;
//	int getLastMeasure();
//};
class WindingEnergyMeasure: public Measure {
	typedef Observable<ScalarObs<double> > obs;
	BoltzWeightType *boltzRatio_;
	obs energy_, energyVar_, wind_, wecov_;
	double L_;
public:
	WindingEnergyMeasure(Lattice *lat, QmcParameters &qmcParametersg,
			BoltzWeightType *boltzRatio);
	WindingEnergyMeasure();
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
	int getLastMeasure();
};

struct KPointData {
	int numOfPoints;
	double obs;
};

rationalNumber calcNorm(IndexType const & kIndex, int L);

class AmpCalcFFT {
	typedef ArrayMulti<double, DIM> ArrayData;
	typedef ArrayData::iterator ArrayDataIter;
	typedef ArrayMulti<complex<double>, DIM> ArrayDataComplex;
	typedef ArrayDataComplex::iterator ArrayDataComplexIter;
	BoltzWeightType *boltzRatio_;
	ArrayData addOne_;
	ArrayDataComplex fftDensity_;
	ArrayData powerSpect_;
	fftwPlan fPlane_;
	Lattice *lat_;
	IndexType latSize_;
	double amp_, ampSqr_;
public:
	void init(Lattice *lat, QmcParameters & qmcParameters,
			BoltzWeightType *boltzRatio);
	void calcAmpFFT();
	void calcAmp();
	ArrayData& getPowerSpect();
	double getAmplitudeVar() const;
	double getAmplitudeVarSqr() const;
};
class AmplitudeMeasureCorrSym: public Measure {
	typedef Observable<VectorObs<double> > obsSym;
	typedef map<rationalNumber, KPointData> KPointDataMap;
	typedef pair<rationalNumber, KPointData> KPointDataPair;
	typedef ArrayMulti<complex<double>, DIM> ArrayDataComplex;
	typedef ArrayDataComplex::iterator ArrayDataComplexIter;
	KPointDataMap kPointDataMap_;
	IndexType latSize_;
	double maxNorm_;
	arma::Col<double> ampSym_;
	obsSym amplitudeCorrSym_;
	AmpCalcFFT &ampCalcFFT_;
	BoltzWeightType *boltzRatio_;
public:
	AmplitudeMeasureCorrSym(QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio, AmpCalcFFT &ampCalcFFT);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
class AmplitudeMeasureCorr: public Measure {
	typedef ArrayObservable<Observable<ScalarObs<double> >, 1> obsArray;
	obsArray amplitudeCorr_;
	int corrSize_;
	AmpCalcFFT &ampCalcFFT_;
	BoltzWeightType *boltzRatio_;
public:
	AmplitudeMeasureCorr(QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio, AmpCalcFFT &ampCalcFFT);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
class AmplitudeMeasureVar: public Measure {
	typedef ScalarBinObs<double> obs;
	obs amplitude_, amplitudesqr_;
	AmpCalcFFT &ampCalcFFT_;
	BoltzWeightType *boltzRatio_;
public:
	AmplitudeMeasureVar(QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio, AmpCalcFFT &ampCalcFFT);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};

class AmplitudeMeasure: public Measure {
	typedef boost::ptr_vector<Measure> MeasureVector;
	MeasureVector measurements_;
	BoltzWeightType *boltzRatio_;
	AmpCalcFFT ampCalcFFT_;
	bool fft_;
public:
	AmplitudeMeasure(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio);
	void DoMeasurement();
	void dumpToFile(hdf5RW &hdf5File) const;
	void readFromFile(hdf5RW &hdf5File);
	bool checkDone() const;
	int getNumOfMeasure() const;
};
/*
 class CurrentMeasure: public Measure {
 typedef ArrayObservable<Observable<ScalarObs<double> >, DIM> obsArray;
 typedef Observable<VectorObs<double> > obsSym;
 typedef Observable<ScalarObs<double> > obs;
 typedef ArrayMulti<double, DIM> ArrayData;
 typedef ArrayData::iterator ArrayDataIter;
 typedef vector<ArrayData> ArrayDataVector;
 typedef ArrayMulti<complex<double>, DIM> ArrayDataComplex;
 typedef ArrayDataComplex::iterator ArrayDataComplexIter;
 typedef vector<ArrayDataComplex> ArrayDataComplexVector;
 typedef ArrayData::iterator ArrayDataComplexVectorIter;
 typedef vector<fftwPlan> fftwPlanArray;
 typedef map<int, KPointData> KPointDataMap;
 typedef pair<int, KPointData> KPointDataPair;
 //obsArray amplitudeCorr_;
 obsSym currentCorrSym_;
 obs kineticEnergy;
 BoltzWeightType boltzRatio_;
 ArrayData current_;
 ArrayDataComplex fftCurrent_;
 fftwPlan fPlane_;
 KPointDataMap kPointDataMap_;
 int L_, T_;
 double maxNorm_;
 arma::Col<double> currentSym_;
 public:
 CurrentMeasure(Lattice *lat, QmcParameters &qmcParameters,
 BoltzWeightType &boltzRatio);
 CurrentMeasure();
 void DoMeasurement();
 void dumpToFile(hdf5RW &hdf5File) const;
 void readFromFile(hdf5RW &hdf5File);
 bool checkDone() const;
 int getNumOfMeasure() const;
 };

 class WindingEnergyMeasure: public Measure {
 typedef Observable<ScalarObs<double> > obs;
 obs wind_, energy_, wecov_;
 BoltzWeightType boltzRatio_;
 double L_;
 public:
 WindingEnergyMeasure(Lattice *lat, QmcParameters &qmcParameters,
 BoltzWeightType boltzRatio);
 WindingEnergyMeasure();
 void DoMeasurement();
 void dumpToFile(hdf5RW &hdf5File) const;
 void readFromFile(hdf5RW &hdf5File);
 bool checkDone() const;
 int getNumOfMeasure() const;
 int getLastMeasure();
 };
 */
class Measurements {
	typedef boost::ptr_vector<Measure> MesureVector;
	typedef MesureVector::iterator MesureVectorIter;
	int numOfBins_;
private:
	MesureVector measures_;
	Lattice *lat_;
	ProgressBar progBar_;
	int rateOpen_, rateClosed_;
	bool allDone_;
	ofstream *outFile_;
public:
	Measurements();
	~Measurements();
	void init(Lattice *lat, QmcParameters &qmcParameters,
			BoltzWeightType *boltzRatio, ofstream *outFile);
	void DoMeasurement(bool &endTime, bool &dumpTime);
	bool checkNotDone() const;
	void dump(String outPath) const;
	void read(String outPath);
	void updateMinNumOfMeasure(int totalNumberOfMeasure);
	void updateClosedRate(int const closedRate);
	int getClosedRate() const;
};
#endif /* MEASURE_H_ */
